package edu.hawaii.halealohacli.jcev.command;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Timer;
import java.util.TimerTask;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.resource.sensordata.jaxb.SensorData;

/**
 * Command: monitor-power [tower | lounge] [interval]
 * 
 * This command prints out a timestamp and the current power for [tower | lounge] every
 * [interval] seconds.  [interval] is an optional integer greater than 0 and defaults to
 * 10 seconds. Entering a carriage return stops this monitoring process and returns the
 * user to the command loop.
 * 
 * @author Micah
 *
 */
public class MonitorPower implements Command {
  
  private static final int defaultInterval = 10;
  private boolean testing = false;
  
  /**
   * Constructor. 
   */
  public MonitorPower() {
     // Input values taken in execute method.
  }

  /**
   * Verifies the arguments, executes the command, and prints the results to the screen.
   * @param args Space delimited string of arguments (i.e. "arg1 arg2 arg3").
   * @param client The WattDepot client that will be performing the queries.
   * @throws Exception If an exception was thrown by the command (i.e. bad arguments)
   * or any of its function calls.
   */
  @Override
  public void execute(String args, WattDepotClient client) throws Exception {
    
    // Checks health of client.
    if (client == null) {
      throw new Exception("Error: Cannot pass null client.");
    }
    if (! client.isHealthy()) {
      throw new Exception("Error: Problem querying data from client. " +
          "Ensure that WattDepot client is valid and internet connection is availible.");
    }
    
    // Checks number of input arguments and throws exception as needed.
    if (args == null) {
      throw new Exception("Error: Input String null.");
    }
    int argumentCount = this.getArgCount(args);
    if (argumentCount < 1 || argumentCount > 2) {
      throw new Exception("Error: Input contains incorrect number of arguments.");
    }
    
    // If interval was given,  that the input interval is valid.
    boolean formatIncorrect = false;
    int interval = MonitorPower.defaultInterval;
    if (argumentCount == 2) {
      try {
        interval = Integer.valueOf(args.substring(args.indexOf(' ') + 1));
      }
      catch (NumberFormatException e) {
        formatIncorrect = true;
      }
    }
    if (formatIncorrect || interval < 0) {
      throw new Exception("Error: Input interval is not valid.");
    }
    
    // Create timer and begin task.
    Timer timer = new Timer();
    String source  = null;
    if (argumentCount == 2) {
      source = args.substring(0, args.indexOf(' ')).trim();
    }
    else {
      source = args.trim();
    }
    
    timer.scheduleAtFixedRate(new MonitorPowerTask(source, client), 0, interval * 1000);
    
    // Stop on user input. Wait a moment for any outstanding queries to resolve, and then return.
    if (! this.testing) {
      new BufferedReader(new InputStreamReader(System.in)).readLine();
    }
    timer.cancel();
    timer.purge();
    Thread.sleep(250);
  }

  /**
   * Returns the string to be printed by the help command.
   * Format:
   * "command-name [args1] [args2] .... \nDescription"
   * @return The string to be printed by the help command.
   */
  @Override
  public String getHelpString() {
      return "monitor-power [tower | lounge] [interval]\n"
        + "Prints a timestamp and current power for [tower | lounge] every [interval] seconds."
        + " Returns upon pressing return.";
  }
  
  /**
   * Helper method. Returns the number of whitespace-delimited words in the input string.
   * If input is null, returns -1.
   * 
   * @param args String to check for word count.
   * @return Number of whitespace-delimited words in the input, or -1 if input is null.
   */
  private int getArgCount(String args) {
    
    String trimmed = args.trim();
    if (trimmed.isEmpty()) {
      return 0;
    }
    
    if (trimmed.indexOf(' ') == -1) {
      return 1;
    }
    else {
      return 1 + getArgCount(trimmed.substring(trimmed.indexOf(' ') + 1));
    }
  }
  
  /**
   * Sets test mode on or off. In test mode, command will exit loop immediately instead
   * of waiting for user input.
   * 
   * @param testing Value to set testing flag to.
   */
  public void setTestMode(boolean testing) {
    this.testing = testing;
  }
}

/**
 * Class that extends TimerTask. Used with MonitorPower to query the client
 * for enegy usage data at constant intervals.
 * 
 * @author Micah
 *
 */
class MonitorPowerTask extends TimerTask {

  private String source = null;
  private WattDepotClient client = null;
  
  /**
   * Constructor.
   * @throws Exception If source name is found to be invalid or client is not healthy.
   * @param source Name of tower/lounge to query.
   * @param client WattDepotClient to use to connect to server.
   */
  public MonitorPowerTask(String source, WattDepotClient client) throws Exception {
    
    // Sets variables.
    this.source = source;
    this.client = client;
    
    // Checks health of client.
    if (client == null) {
      throw new Exception("Error: Cannot pass null client.");
    }
    if (! client.isHealthy()) {
      throw new Exception("Error: Problem querying data from client. " +
          "Ensure that WattDepot client is valid and internet connection is availible.");
    }
    
    // Checks that source is not null.
    if (source == null) {
      throw new Exception("Error: Input String null.");
    }
    
    // Ensures that source is valid.
    boolean sourceInvalid = false;
    try {
      client.getSource(this.source);
    }
    catch (Exception e) {
      sourceInvalid = true;
    }
    
    if (sourceInvalid) {
      throw new Exception("Error: Input source is invalid.");
    }
  }
  
  /**
   * Run method. Prints energy usage data of a requested source with a timestamp at intervals.
   */
  @Override
  public void run() {
    SensorData data = null;
    try {
      data = client.getLatestSensorData(source);
    }
    catch (Exception e) {
      System.out.println("Error: Problem retrieving data from client.");
    }
    
    if (data != null) {
      System.out.println("Timestamp: " + data.getTimestamp().toString() + "\nEnergy: " +
          data.getProperty(SensorData.POWER_CONSUMED) + "\n");
    }
  }
  
}
